from typing import List, Optional, Tuple
from fastapi import HTTPException
from sqlalchemy.orm import selectinload
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, insert, update, delete, and_
from sqlalchemy.exc import NoResultFound

from basic.models import (
    LogicalVersion,
    LogicalFile,
    LogicalEntry,
    LogicalRepository,
    RelationEntryFile,
    RelationFileVersion,
    RelationRepoEntry,
)
from basic.database import generate_random_string

def get_parent_path(path: str) -> str | None:
    def _remove_trailing_slash(path: str) -> str:
        if path == "/":
            return path
        if path.endswith("/"):
            path = path[:-1]
        return path
    
    path = _remove_trailing_slash(path)
    if path == "/":
        return None
    last_slash_index = path.rfind("/")
    if last_slash_index == -1:
        return path
    path = path[:last_slash_index+1]
    # /XXXX/ -> /XXXX 
    return _remove_trailing_slash(path)

async def check_exist_same_path(db: AsyncSession, path: str, repo_key: str) -> LogicalEntry | None:
    try:
        check_stmt = (
            select(LogicalEntry)
            .select_from(LogicalEntry)
            .join(
                RelationRepoEntry,
                RelationRepoEntry.entry_key == LogicalEntry.key
            )
            .where(
                and_(
                    LogicalEntry.path == path,
                    RelationRepoEntry.repository_key == repo_key
                )
            )
        )
        check_result = await db.execute(check_stmt)
        existing_entry = check_result.scalar_one_or_none()
        return existing_entry
    except Exception as e:
        print(f"Error checking existing entry: {e}")
        raise e

async def check_parent_exist(db: AsyncSession, path: str, repo_key: str) -> Tuple[bool, int | None]:
    try:
        parent_path = get_parent_path(path)
        if parent_path is None:
            return True, None
            
        stmt = (
            select(LogicalEntry)
            .select_from(LogicalEntry)
            .join(
                RelationRepoEntry,
                RelationRepoEntry.entry_key == LogicalEntry.key
            )
            .where(
                and_(
                    LogicalEntry.path == parent_path,
                    LogicalEntry.is_directory == True,
                    RelationRepoEntry.repository_key == repo_key
                )
            )
        )
        result = await db.execute(stmt)
        parent = result.scalar_one_or_none()
        
        if parent:
            return True, parent.id
        else:
            return False, None
            
    except Exception as e:
        print(f"Error checking parent directory: {e}")
        raise e

async def create_new_entry(
    db: AsyncSession,
    path: str, 
    is_directory: bool, 
    parent_id: int | None, 
    repo_key: str
) -> LogicalEntry:
    # 创建新条目
    new_key = generate_random_string(16)
    stmt = (
        insert(LogicalEntry)
        .values(
            key=new_key,
            path=path,
            is_directory=is_directory,
            parent_id=parent_id,
        )
        .returning(LogicalEntry)
    )
    result = await db.execute(stmt)
    new_entry = result.scalar_one()
    
    # 创建关联
    stmt = insert(RelationRepoEntry).values(
        repository_key=repo_key, 
        entry_key=new_key
    )
    
    await db.execute(stmt)
    return new_entry

async def delete_entry(db: AsyncSession, key: str) -> bool:
    try:
        # 查找条目
        stmt = select(LogicalEntry).where(LogicalEntry.key == key)
        result = await db.execute(stmt)
        entry = result.scalar_one()
        
        # 删除关联
        stmt = delete(RelationRepoEntry).where(RelationRepoEntry.entry_key == key)
        await db.execute(stmt)
        
        # 删除条目
        await db.delete(entry)
        return True
    except Exception as e:
        print(f"Error deleting entry: {e}")
        return False

async def get_children_entries(db: AsyncSession, entry_key: str) -> List[LogicalEntry]:
    try:
        # 获取父条目
        parent_stmt = select(LogicalEntry).where(LogicalEntry.key == entry_key)
        parent_result = await db.execute(parent_stmt)
        parent = parent_result.scalar_one()

        # 获取子条目
        stmt = (
            select(LogicalEntry)
            .where(LogicalEntry.parent_id == parent.id)
            .options(selectinload(LogicalEntry.file))
        )
        
        result = await db.execute(stmt)
        entries = result.scalars().all()
        return entries
        
    except Exception as e:
        print(f"Error getting children entries: {e}")
        raise e

async def move_entry(db: AsyncSession, key: str, path: str) -> LogicalEntry:
    try:
        parent_id = None
        if get_parent_path(path) is not None:
            # 检查目标父目录是否存在
            stmt = select(LogicalEntry).where(
                and_(
                    LogicalEntry.path == get_parent_path(path),
                    LogicalEntry.is_directory == True
                )
            )
            result = await db.execute(stmt)
            try:
                parent = result.scalar_one()
                parent_id = parent.id
            except NoResultFound:
                raise HTTPException(
                    status_code=400,
                    detail="Target parent directory does not exist",
                )
        # 更新条目
        stmt = (
            update(LogicalEntry)
            .where(LogicalEntry.key == key)
            .values(path=path, parent_id=parent_id)
            .returning(LogicalEntry)
        )
        result = await db.execute(stmt)
        updated_entry = result.scalar_one()
        return updated_entry
    except Exception as e:
        print(f"Error moving entry: {e}")
        raise e